package personal.lijun.skeleton.web.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import personal.lijun.skeleton.web.authentication.CalendarUserAuthenticationProvider;

/**
 * @Auther: lijun@sstcsoft.com
 * @Date: 2019/9/16 18:06
 */
@Configuration
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
//@EnableGlobalAuthentication
@Import({CustomAuthorizationConfig.class})

// Thymeleaf needs to use the Thymeleaf configured FilterSecurityInterceptor
// and not the default Filter from AutoConfiguration.
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger logger = LoggerFactory
            .getLogger(SecurityConfig.class);


    @Autowired
    private CalendarUserAuthenticationProvider calendarUserAuthenticationProvider;

    @Autowired
    private FilterInvocationSecurityMetadataSource metadataSource;

    @Autowired
    private AccessDecisionManager accessDecisionManager;

    /**
     * BCryptPasswordEncoder password encoder
     * @return
     */
    @Description("Standard PasswordEncoder")
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder(4);
    }

    @Override
    public void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(calendarUserAuthenticationProvider);
    }

    /**
     * This is the equivalent to:
     * <pre>
     *     <http pattern="/resources/**" security="none"/>
     *     <http pattern="/css/**" security="none"/>
     *     <http pattern="/webjars/**" security="none"/>
     * </pre>
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(final WebSecurity web) throws Exception {

        // Ignore static resources and webjars from Spring Security
        web.ignoring()
                .antMatchers("/resources/**")
                .antMatchers("/css/**")
                .antMatchers("/webjars/**")
        ;

        // Thymeleaf needs to use the Thymeleaf configured FilterSecurityInterceptor
        // and not the default Filter from AutoConfiguration.
        final HttpSecurity http = getHttp();
        web.postBuildAction(() -> {
            FilterSecurityInterceptor filterSecurityInterceptor =http.getSharedObject(FilterSecurityInterceptor.class);
            filterSecurityInterceptor.setSecurityMetadataSource(metadataSource);
            web.securityInterceptor(filterSecurityInterceptor);
        });
    }

    /**
     * HTTP Security configuration
     *
     * <pre><http auto-config="true"></pre> is equivalent to:
     * <pre>
     *  <http>
     *      <form-login />
     *      <http-basic />
     *      <logout />
     *  </http>
     * </pre>
     *
     * Which is equivalent to the following JavaConfig:
     *
     * <pre>
     *     http.formLogin()
     *          .and().httpBasic()
     *          .and().logout();
     * </pre>
     *
     * @param http HttpSecurity configuration.
     * @throws Exception Authentication configuration exception
     *
     * @see <a href="http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-jc.html">
     *     Spring Security 3 to 4 migration</a>
     */
    @Override
    protected void configure(final HttpSecurity http) throws Exception {

        // Access Decision Manager
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .accessDecisionManager(accessDecisionManager)
        ;

        // Matching
        http.authorizeRequests()
                // FIXME: TODO: Allow anyone to use H2 (NOTE: NOT FOR PRODUCTION USE EVER !!! )
                .antMatchers("/","/actuator/**").permitAll();
//                .antMatchers("/login/*").permitAll()
//                .antMatchers("/logout").permitAll()
//                .antMatchers("/signup/*").permitAll()
//                .antMatchers("/errors/**").permitAll()
//                .antMatchers("/admin1/default").access("isLocal() and hasRole(\"ADMIN\")")
//                .antMatchers("/admin/*").access("hasRole('ADMIN') and isFullyAuthenticated()")
//                .antMatchers("/admin/**").access("isLocal() and hasRole('ADMIN')")
//                .antMatchers("/events/").hasRole("ADMIN")
//                .antMatchers("/**").hasRole("USER");

        //<expression-handler ref="customWebSecurityExpressionHandler"/>
//        http.authorizeRequests().antMatchers("/acuator/**").permitAll();
        // Login
        http.formLogin()
                .loginPage("/login/form")
                .loginProcessingUrl("/login")
                .failureUrl("/login/form?error")
                .usernameParameter("username")
                .passwordParameter("password")
                .defaultSuccessUrl("/default", true)
                .permitAll();

        // Logout
        http.logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login/form?logout").deleteCookies("JSESSIONID").invalidateHttpSession(true)
                .permitAll();

        // Anonymous
        http.anonymous();

        // CSRF is enabled by default, with Java Config
        http.csrf().disable();

        // Exception Handling
//        http.exceptionHandling()
//                .accessDeniedPage("/errors/403")
//        ;

        // Enable <frameset> in order to use H2 web console
        http.headers().frameOptions().disable();

        http.sessionManagement()
                .sessionFixation()
                .migrateSession()
                .maximumSessions(1)
                .expiredUrl("/login/form?expired");
    }

}
